package com.slimerevenge.model;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.Joint;
import com.badlogic.gdx.physics.box2d.JointDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.joints.DistanceJoint;
import com.badlogic.gdx.physics.box2d.joints.DistanceJointDef;
import com.badlogic.gdx.physics.box2d.joints.WeldJointDef;
import com.badlogic.gdx.utils.Array;
import com.slimerevenge.view.WorldRenderer;


public class Player {
	
	public enum State {
		LAND, AIR
	}

	final int NUM_SEGMENTS = 16;
	final float SPRING = 10f;
	
	public static final float SPEED = 2f;
	public static final float JUMP_VELOCITY = 1f;
	public static final float GRAVITY_SCALE = 3f;
	public static final float SIZE = 0.9f;
	private PlayerGestureDetector gestureDetector;
	
	/**
	 * Since we are using box2d, we will be using their version of 'Player'
	 */
	private Body player;
	private Array<Body> subBodyArr = new Array<Body>();
	private Array<Joint> jointsArr = new Array<Joint>();
	private Array<JointDef> jointsDefArr = new Array<JointDef>();
	private Texture playerTexture;
	
	//Properties of player
	Vector2 acceleration = new Vector2();
	Vector2 velocity = new Vector2();
	Rectangle bounds = new Rectangle();
	State state = State.LAND;
	boolean facingLeft = true;
	Vector2 position = new Vector2();
	float hSpeed = 0.0f;
	
	Vector2 cursorPosition = new Vector2(0,0);
	
	/**
	 * Player instance
	 * @param position Position of the player when level starts
	 * @param world Level that this player is in
	 */
	public Player(Vector2 position, World world) {

		//This is for local reference for player
		this.position = position;
		this.bounds.height = SIZE;
		this.bounds.width = SIZE;
		gestureDetector = new PlayerGestureDetector();
		playerTexture = new Texture(Gdx.files.internal("sprite/slime.png"));
		
		//This part onwards is related to box2d's player initialization
		BodyDef playerBodyDef = new BodyDef();
		//We set our body to dynamic, for something like ground which doesn't move we would set it to StaticBody
		playerBodyDef.type = BodyDef.BodyType.DynamicBody;
		playerBodyDef.position.set(this.position.x, this.position.y);
		player = world.createBody(playerBodyDef);
		
		CircleShape playerShape = new CircleShape();
		//playerShape.setAsBox(this.bounds.width / (2 * WorldRenderer.PIXELS_PER_TILE),
		//		this.bounds.height / (2 * WorldRenderer.PIXELS_PER_TILE));
		//playerShape.setAsBox(this.bounds.width/2, this.bounds.height/2);
		playerShape.setRadius(SIZE/6f);
		player.setFixedRotation(true);
		
		//Create a fixture and attach it to player
		FixtureDef playerFixtureDef = new FixtureDef();
		playerFixtureDef.shape = playerShape;
		playerFixtureDef.restitution = 0.1f;
		playerFixtureDef.density = 0.2f;
		playerFixtureDef.friction = 1.0f;

		player.createFixture(playerFixtureDef);
		for(int i=0;i<NUM_SEGMENTS;i++){
	        float subx = MathUtils.cosDeg(360/NUM_SEGMENTS*i)/4f;
	        float suby = MathUtils.sinDeg(360/NUM_SEGMENTS*i)/4f;
	        Body subBody;
	        BodyDef subBodyDef = new BodyDef();
	        subBodyDef.type = BodyDef.BodyType.DynamicBody;
	        
	        CircleShape subPlayerShape = new CircleShape();
	        subPlayerShape.setRadius(SIZE/10f);
	        //PolygonShape subPlayerShape = new PolygonShape();
	        //subPlayerShape.setAsBox(SIZE/8f, SIZE/8f);
	        
			subBodyDef.position.set(this.position.x+subx, this.position.y+suby);
			subBody = world.createBody(subBodyDef);
			
			FixtureDef subPlayerFixtureDef = new FixtureDef();
			subPlayerFixtureDef.shape = subPlayerShape;
			subPlayerFixtureDef.density = 0.2f;
			subPlayerFixtureDef.restitution = 0f;
			subPlayerFixtureDef.friction = 1f;     
			
	        subBody.createFixture(subPlayerFixtureDef);
	        
	        subBodyArr.add(subBody);
	        
	        subPlayerShape.dispose();
		}
		for(int i=0;i<NUM_SEGMENTS;i++){
	        float subx = MathUtils.cosDeg(360/NUM_SEGMENTS*i)/2f;
	        float suby = MathUtils.sinDeg(360/NUM_SEGMENTS*i)/2f;
	        Body subBody;
	        BodyDef subBodyDef = new BodyDef();
	        subBodyDef.type = BodyDef.BodyType.DynamicBody;
	        
	        CircleShape subPlayerShape = new CircleShape();
	        subPlayerShape.setRadius(SIZE/6f);
	        //PolygonShape subPlayerShape = new PolygonShape();
	        //subPlayerShape.setAsBox(SIZE/8f, SIZE/8f);
	        
			subBodyDef.position.set(this.position.x+subx, this.position.y+suby);
			subBody = world.createBody(subBodyDef);
			
			FixtureDef subPlayerFixtureDef = new FixtureDef();
			subPlayerFixtureDef.shape = subPlayerShape;
			subPlayerFixtureDef.density = 0.2f;
			subPlayerFixtureDef.restitution = 0f;
			subPlayerFixtureDef.friction = 1f;     
			
	        subBody.createFixture(subPlayerFixtureDef);
	        
	        subBodyArr.add(subBody);
	        
	        subPlayerShape.dispose();
		}
		for (int i = 0; i < NUM_SEGMENTS; i++) {
		    int neighborIndex = (i + 1) % NUM_SEGMENTS;

		    WeldJointDef joint1 = new WeldJointDef();
		    joint1.initialize(player, subBodyArr.get(i), player.getPosition());
			//joint1.bodyB = player;
			//joint1.bodyA = subBodyArr.get(i);
			//joint1.initialize(player, subBodyArr.get(i), player.getPosition(), subBodyArr.get(i).getPosition());
			//joint1.collideConnected = true;
			//joint1.frequencyHz = 0;
			//joint1.dampingRatio = 0f;
			//joint1.length = 0.3f;

			jointsDefArr.add(joint1);
			jointsArr.add(world.createJoint(joint1));

			WeldJointDef joint2 = new WeldJointDef();
			joint2.initialize(subBodyArr.get(neighborIndex), subBodyArr.get(i), player.getPosition());
			//joint2.bodyA = subBodyArr.get(neighborIndex);
			//joint2.bodyB = subBodyArr.get(i);
			//joint2.initialize(subBodyArr.get(neighborIndex), subBodyArr.get(i), subBodyArr.get(neighborIndex).getPosition(), subBodyArr.get(i).getPosition());
			//joint2.collideConnected = true;
			//joint2.frequencyHz = 0;
			//joint2.dampingRatio = 0f;
			//joint2.length = 0.067f;

			jointsDefArr.add(joint2);
			jointsArr.add(world.createJoint(joint2));
			
		}
		
		for(int i=NUM_SEGMENTS;i<NUM_SEGMENTS*2;i++)
		{
			int neighborIndex = (i - NUM_SEGMENTS + 1) % (NUM_SEGMENTS) + NUM_SEGMENTS;
			int primaryIndex = i-NUM_SEGMENTS;
			
			//Vector2 outerPosition = new Vector2(MathUtils.cosDeg(360/NUM_SEGMENTS*i),MathUtils.sinDeg(360/NUM_SEGMENTS*i));
			
			DistanceJointDef joint1 = new DistanceJointDef();
			joint1.initialize(subBodyArr.get(primaryIndex), subBodyArr.get(i), subBodyArr.get(primaryIndex).getPosition(), subBodyArr.get(i).getPosition());
//			joint1.bodyA = subBodyArr.get(primaryIndex);
//			joint1.bodyB = subBodyArr.get(i);
			joint1.collideConnected = false;
			joint1.frequencyHz = SPRING;
			joint1.dampingRatio = 1f;
//			joint1.length = 0.1f;

			jointsDefArr.add(joint1);
			jointsArr.add(world.createJoint(joint1));

			DistanceJointDef joint2 = new DistanceJointDef();
			joint2.initialize(subBodyArr.get(neighborIndex), subBodyArr.get(i), subBodyArr.get(neighborIndex).getPosition(), subBodyArr.get(i).getPosition());
//			joint2.bodyA = subBodyArr.get(neighborIndex);
//			joint2.bodyB = subBodyArr.get(i);
			joint2.collideConnected = true;
			joint2.frequencyHz = 0;
			joint2.dampingRatio = 0f;
			joint2.length = 0.15f;

			jointsDefArr.add(joint2);
			jointsArr.add(world.createJoint(joint2));
		}
		
		//player.getFixtureList().get(0).setUserData("player");
		playerShape.dispose();
		
		player.setGravityScale(GRAVITY_SCALE);
	}

	public void maintainLimit(){
		for(int i=0;i<subBodyArr.size;i++)
		{
			if(i<subBodyArr.size/2)
			{
		        float subx = this.position.x + MathUtils.cosDeg(360/NUM_SEGMENTS*i)/4f;
		        float suby = this.position.y +  MathUtils.sinDeg(360/NUM_SEGMENTS*i)/4f;
		        subBodyArr.get(i).setTransform(subx,suby, subBodyArr.get(i).getAngle());
			}
			else
			{
		        float subx = this.position.x + MathUtils.cosDeg(360/NUM_SEGMENTS*i)/2f;
		        float suby = this.position.y + MathUtils.sinDeg(360/NUM_SEGMENTS*i)/2f;
		        subBodyArr.get(i).setTransform(subx,suby, subBodyArr.get(i).getAngle());
			}
		}
	}
	
	//setters
	public void setPosition(Vector2 position){
		this.position = position;
		this.getBody().setTransform(position, this.getBody().getAngle());
	}
	
	public void setState(State newState){
	    this.state = newState;
	}
	
	//getters
	public Vector2 getPosition() {
		return this.getBody().getPosition();	
	}
	
	public PlayerGestureDetector getGestureDetector(){
		return gestureDetector;
	}
	
	public Rectangle getBounds() {
		return bounds;
	}
	
	public Body getBody(){
		return player;
	}
	
	public Array<Body> getSubBodyArray() {
		return subBodyArr;
	}
	
	public Array<JointDef> getJointsDefArr() {
		return jointsDefArr;
	}


	public Texture getTexture(){
		return playerTexture;
	}
	
	
	/**
	 * Detects input from user
	 */
	public class PlayerGestureDetector implements InputProcessor {
		
		@Override
		public boolean touchDown(int x, int y, int pointer, int button) {
			
			Gdx.app.log("touchDowned", Gdx.input.getX() + " and " + Gdx.input.getY());
			cursorPosition.set(x, y);
			
			/*if(CameraController.getCamHeight()/2>Gdx.input.getY())
			{
				//Jump
				player.setLinearVelocity(new Vector2(player.getLinearVelocity().x,16f));
				//player.applyLinearImpulse(new Vector2(player.getLinearVelocity().x,10f), player.getWorldCenter(), true);
			}
			else if(CameraController.getCamHeight()/2<Gdx.input.getY())
			{
				if(CameraController.getCamWidth()/2>Gdx.input.getX())
				{
					player.setLinearVelocity(new Vector2(-3,player.getLinearVelocity().y));
					
				}
				else if(CameraController.getCamWidth()/2<Gdx.input.getX())
				{
					player.setLinearVelocity(new Vector2(3,player.getLinearVelocity().y));
				}
			}*/
			
			return true;
		}

		@Override
		public boolean touchDragged(int x, int y, int pointer) {	
			//Gdx.app.log("touchDragged", Gdx.input.getX() + " and " + Gdx.input.getY());
			if(pointer==1)
			{
				Gdx.app.log("Multitouched:", x + " and " +y);
				player.setTransform( Gdx.input.getX()/480f*10f, Gdx.input.getY()/320f*7, 0);
			}
			
			return true;
		}

		@Override
		public boolean touchUp(int x, int y, int pointer, int button) {
			//Gdx.app.log("touchUped", Gdx.input.getX() + " and " + Gdx.input.getY());
			float xdiff = (cursorPosition.x-x)/10f;//*3.75f;
			float ydiff = (y-cursorPosition.y)/10f;//*6.25f;
			
			if(xdiff>10)
				xdiff = 10;
			else if(xdiff<-10)
				xdiff = -10;
			if(ydiff>20)
				ydiff = 20;
			else if(ydiff<-20)
				ydiff = -20;
			
			float totalMass = player.getMass();
			for(Body subBody:subBodyArr)
			{
				totalMass += subBody.getMass();
			}
			//player.setLinearVelocity(new Vector2(xdiff,ydiff));
			//player.applyForceToCenter(new Vector2(xdiff,ydiff), true);
			
			player.applyLinearImpulse(new Vector2(xdiff*totalMass,ydiff*totalMass), player.getPosition(), false);
			return true;
		}

		//NOT USING
		@Override
		public boolean keyDown(int keycode) {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public boolean keyUp(int keycode) {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public boolean keyTyped(char character) {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public boolean mouseMoved(int screenX, int screenY) {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public boolean scrolled(int amount) {
			// TODO Auto-generated method stub
			return false;
		}

	}

}


